﻿
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Linq;


namespace Boilen.Primitives.Members {

    /// <summary>
    /// Writes code for a constant declaration.
    /// </summary>
    public sealed class AttributeMember : Member {

        public static readonly AttributeMember EmptyAttribute = new AttributeMember( );

        /// <summary>Declares the <see cref="EditorBrowsableAttribute"/> with the <see cref="EditorBrowsableState.Never"/> state.</summary>
        public static readonly AttributeMember EditorBrowsableNever;

        /// <summary>Declares the <see cref="SuppressMessageAttribute"/> for unused parameters on partial helper methods.</summary>
        public static readonly AttributeMember SuppressUnusedPartialParameters;

        /// <summary>Declares the <see cref="SuppressMessageAttribute"/> for static members in generic types.</summary>
        public static readonly AttributeMember SuppressStaticMembersInGenericTypes;

        private const string AttributeSuffix = "Attribute";
        private static readonly string SuppressMessageTypeName;

        private readonly bool isEmpty_;
        private readonly string[] positionalArguments_;
        private readonly Dictionary<string, string> namedArguments_ = new Dictionary<string, string>( );


        /// <inheritdoc/>
        public override Extensions UsedExtensions { get { return Extensions.None; } }

        /// <summary>
        /// Gets the positional arguments of the attribute.
        /// </summary>
        public IEnumerable<string> PositionalArguments { get { return this.positionalArguments_.AsEnumerable( ); } }

        /// <summary>
        /// Gets the named arguments of the attribute.
        /// </summary>
        public IDictionary<string, string> NamedArguments { get { return this.namedArguments_; } }


        static AttributeMember( ) {
            SuppressMessageTypeName = typeof( SuppressMessageAttribute ).FullName;

            EditorBrowsableNever = new AttributeMember(
                typeof( EditorBrowsableAttribute ).FullName,
                typeof( EditorBrowsableState ).FullName + "." + EditorBrowsableState.Never
            );

            SuppressUnusedPartialParameters = AttributeMember.SuppressMessage( "Microsoft.Usage", "CA1801:ReviewUnusedParameters", "Partial method definitions are not required to use method parameters." );
            SuppressStaticMembersInGenericTypes = AttributeMember.SuppressMessage( "Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes", "Part of standard property implementation." );
        }

        /// <summary>
        /// Initializes a new <see cref="AttributeMember"/> instance.
        /// </summary>
        /// <param name="name">The name of the attribute type.</param>
        /// <param name="positionalArguments">The positional arguments of the attribute.</param>
        private AttributeMember( string name, params string[] positionalArguments )
            : base( name ) {
            Ensure.NotNull( (object)positionalArguments );

            this.positionalArguments_ = positionalArguments;
            this.isEmpty_ = false;
        }

        /// <summary>
        /// Initializes a new empty <see cref="AttributeMember"/> instance.
        /// </summary>
        private AttributeMember( )
            : base( Member.Empty.Name ) {
            this.positionalArguments_ = new string[0];
            this.isEmpty_ = true;
        }


        public static AttributeMember Custom(string typeName, params string[] positionalArguments)
        {
            if( typeName.EndsWith( AttributeSuffix ) )
                typeName = typeName.Substring( 0, typeName.Length - AttributeSuffix.Length );

            return new AttributeMember(typeName, positionalArguments);
        }

        public static AttributeMember SuppressMessage( string category, string id, string justification ) {
            id = TypeRepository.EscapeString( id );
            category = TypeRepository.EscapeString( category );
            justification = TypeRepository.EscapeString( justification );

            return new AttributeMember( SuppressMessageTypeName, category, id ) {
                NamedArguments = { { "Justification", justification } }
            };
        }

        public static AttributeMember SuppressMessage( string category, string id, string messageId, string justification ) {
            messageId = TypeRepository.EscapeString( messageId );

            var attribute = SuppressMessage( category, id, justification );
            attribute.NamedArguments.Add( "MessageId", messageId );

            return attribute;
        }


        /// <inheritdoc/>
        protected override void WriteCore( ICodeWriter writer ) {
            if( this.isEmpty_ )
                return;

            using( Enclose.Brackets( writer ) ) {
                writer.Write( this.Name );
                using( Enclose.Parenthesis( writer ) ) {
                    Util.Iterate(
                        this.PositionalArguments,
                        ( i, last ) => writer.Write( ", " ),
                        ( i, arg ) => writer.Write( arg )
                    );

                    if( this.PositionalArguments.Any( ) && this.NamedArguments.Any( ) )
                        writer.Write( ", " );
                    Util.Iterate(
                        this.NamedArguments,
                        ( i, last ) => writer.Write( ", " ),
                        ( i, arg ) => writer.Write( arg.Key + " = " + arg.Value )
                    );
                }
            }
            writer.WriteLine( );
        }

    }

}
